﻿using BepInEx.Logging;
using CheatMenu.Managers;
using CheatMenu.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace CheatMenu.Menus
{
    public abstract class MenuBase
    {
        private readonly int windowID;
        private bool initializedGUI;
        private bool isOffsetDirty;

        protected bool ForceGUIEnabled { get; set; }
        protected Rect WindowRect { get; private set; }
        private Vector2 windowOffset;
        protected Vector2 WindowOffset
        {
            get => windowOffset;
            set
            {
                if (windowOffset != value)
                    isOffsetDirty = true;

                windowOffset = value;
            }
        }

        public string Name { get; private set; }
        public string Text { get; set; }
        public bool Draggable { get; set; }
        public Vector2 Size { get; set; }
        public Alignment Alignment { get; set; } = Alignment.None;

        private bool shown;
        public bool Shown
        {
            get => shown;
            set
            {
                bool lastShown = shown;
                shown = value;

                if (lastShown != shown)
                {
                    if (shown)
                        OnShown();
                    else
                        OnHidden();
                }
            }
        }

        protected MenuBase(string name, string text = null)
        {
            Name = name;
            Text = !string.IsNullOrEmpty(text) ? text : name;
            windowID = GetHashCode();
            Size = InitializeSize();

            MenuManager.AddMenu(this);
        }

        public void OnGUI()
        {
            if (!initializedGUI)
            {
                InitializeGUI();
                Size = InitializeSize();
                UpdateWindowSize();
                initializedGUI = true;
            }
            else if (isOffsetDirty)
            {
                UpdateWindowSize();
            }

            GUIUtil.DrawRoundedRectangle(WindowRect);
            WindowRect = GUILayout.Window(windowID, WindowRect, OnMenuWindow, Text);
        }

        public void ToggleShown() => Shown = !Shown;

        public void ReinitializeGUI() => initializedGUI = false;

        private void OnMenuWindow(int id)
        {
            try
            {
                GUI.enabled = ForceGUIEnabled ? true : StateManager.GUIEnabled;
                MenuWindow(id);
            }
            catch (Exception ex)
            {
                LogManager.LogError($"[{Name}] GUI Exception: {ex}");
            }
            if (Draggable) GUI.DragWindow();
        }

        public void UpdateWindowSize()
        {
            Rect screenRect = GUIManager.GetWorkingArea();

            float w = Size.x > 0 ? Size.x : screenRect.width;
            float h = Size.y > 0 ? Size.y : screenRect.height;
            if (Alignment != Alignment.None)
                WindowRect = GUIUtil.AlignRect(w, h, screenRect, Alignment);
            else
                WindowRect = new Rect(screenRect.x + windowOffset.x, screenRect.y + windowOffset.y, w, h);

            isOffsetDirty = false;
        }

        public void Focus()
        {
            GUI.FocusWindow(windowID);
        }

        public void BringWindowToFront()
        {
            GUI.BringWindowToFront(windowID);
        }

        protected virtual void InitializeGUI() { }
        protected virtual void OnShown() { }
        protected virtual void OnHidden() { }

        protected abstract Vector2 InitializeSize();
        protected abstract void MenuWindow(int id);
    }
}
